package org.example.mq.common.network.mqclient;

import org.example.mq.common.BinaryTool;
import org.example.mq.common.Consumer;
import org.example.mq.common.MqException;
import org.example.mq.common.Request;
import org.example.mq.common.network.mqservice.BasicArguments;
import org.example.mq.common.network.mqservice.BasicReturns;
import org.example.mq.common.network.mqservice.Exchange.ExchangeDeclareArguments;
import org.example.mq.common.network.mqservice.Exchange.ExchangeDeleteArguments;
import org.example.mq.common.network.mqservice.MSGQueue.QueueDeclareArguments;
import org.example.mq.common.network.mqservice.MSGQueue.QueueDeleteArguments;
import org.example.mq.common.network.mqservice.Message.BasicAckArguments;
import org.example.mq.common.network.mqservice.Message.BasicConsumeArguments;
import org.example.mq.common.network.mqservice.Message.BasicPublicArguments;
import org.example.mq.common.network.mqservice.QueueBind.QueueBindArguments;
import org.example.mq.common.network.mqservice.QueueBind.QueueUnBindArguments;
import org.example.mq.mqserver.core.BasicProperties;
import org.example.mq.mqserver.coreenum.ExchangeType;

import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class Channel {

    //用于表示channel
    private String channelId ;

    //表示当前channel属于哪一个连接
    private Connection connection;

    //存储收到的服务器响应（服务器，响应）（类似于tcp连接中的缓冲区）
    private ConcurrentHashMap<String, BasicReturns> basicReturnsMap = new ConcurrentHashMap<>();

    //如果当前channel订阅了某个队列，需要记录回调
    //一个channel只能有一个回调
    private Consumer consumer = null;

    public String getChannelId() {
        return channelId;
    }

    public void setChannelId(String channelId) {
        this.channelId = channelId;
    }

    public Connection getConnection() {
        return connection;
    }

    public void setConnection(Connection connection) {
        this.connection = connection;
    }

    public ConcurrentHashMap<String, BasicReturns> getBasicReturnsMap() {
        return basicReturnsMap;
    }

    public void setBasicReturnsMap(ConcurrentHashMap<String, BasicReturns> basicReturnsMap) {
        this.basicReturnsMap = basicReturnsMap;
    }

    public Consumer getConsumer() {
        return consumer;
    }

    public void setConsumer(Consumer consumer) {
        this.consumer = consumer;
    }

    public Channel(String channelId, Connection connection) {
        this.channelId = channelId;
        this.connection = connection;
    }




    /*
    * 创建Channel
    * */
    public boolean createChannel() throws IOException {
        BasicArguments basicArguments = new BasicArguments();
        basicArguments.setChannelId(channelId);
        basicArguments.setRid(generateRid());
        byte[] payload = BinaryTool.toBytes(basicArguments);

        Request request = new Request();
        request.setType(0x1);
        request.setLength(payload.length);
        request.setPayload(payload);

        //发送请求
        connection.writeRequest(request);
        //等待服务器的回复
        BasicReturns basicReturns = waitResult(basicArguments.getRid());
        return basicReturns.isOk();

    }

    /*
    * 删除Channel
    * */
    public boolean close() throws IOException {
        BasicArguments basicArguments = new BasicArguments();
        basicArguments.setRid(generateRid());
        basicArguments.setChannelId(channelId);
        byte[] payload = BinaryTool.toBytes(basicArguments);

        Request request = new Request();
        request.setType(0x2);
        request.setLength(payload.length);
        request.setPayload(payload);

        connection.writeRequest(request);
        BasicReturns basicReturns = waitResult(basicArguments.getRid());
        return basicReturns.isOk();
    }


    /*
    * 创建交换机
    * */
    public boolean exchangeDeclare(String exchangeName, ExchangeType exchangeType, boolean durable, boolean autoDelete,
                                   Map<String,Object> arguments) throws IOException {
        ExchangeDeclareArguments exchangeDeclareArguments = new ExchangeDeclareArguments();
        exchangeDeclareArguments.setRid(generateRid());
        exchangeDeclareArguments.setChannelId(channelId);
        exchangeDeclareArguments.setExchangeName(exchangeName);
        exchangeDeclareArguments.setExchangeType(exchangeType);
        exchangeDeclareArguments.setDurable(durable);
        exchangeDeclareArguments.setAutoDelete(autoDelete);
        exchangeDeclareArguments.setArguments(arguments);
        byte[] payload = BinaryTool.toBytes(exchangeDeclareArguments);

        Request request = new Request();
        request.setType(0x3);
        request.setLength(payload.length);
        request.setPayload(payload);

        //发送请求
        connection.writeRequest(request);
        //接收返回的响应
        BasicReturns basicReturns = waitResult(exchangeDeclareArguments.getRid());
        return basicReturns.isOk();
    }

    /*
    * 删除交换机
    * */
    public boolean exchangeDelete(String exchangeName) throws IOException {
        ExchangeDeleteArguments exchangeDeleteArguments  = new ExchangeDeleteArguments();
        exchangeDeleteArguments.setRid(generateRid());
        exchangeDeleteArguments.setChannelId(channelId);
        exchangeDeleteArguments.setExchangeName(exchangeName);
        byte[] payload = BinaryTool.toBytes(exchangeDeleteArguments);

        Request request = new Request();
        request.setType(0x4);
        request.setLength(payload.length);
        request.setPayload(payload);

        //发送请求
        connection.writeRequest(request);
        //接收返回的响应
        BasicReturns basicReturns = waitResult(exchangeDeleteArguments.getRid());
        return basicReturns.isOk();
    }

    /*
    * 增加队列
    * */
    public boolean queueDeclare(String queueName,boolean durable,boolean exclusive,boolean autoDelete,
                                Map<String,Object> arguments) throws IOException {
        QueueDeclareArguments queueDeclareArguments = new QueueDeclareArguments();
        queueDeclareArguments.setRid(generateRid());
        queueDeclareArguments.setChannelId(channelId);
        queueDeclareArguments.setQueueName(queueName);
        queueDeclareArguments.setDurable(durable);
        queueDeclareArguments.setExclusive(exclusive);
        queueDeclareArguments.setAutoDelete(autoDelete);
        queueDeclareArguments.setArguments(arguments);
        byte[] payload = BinaryTool.toBytes(queueDeclareArguments);

        Request request = new Request();
        request.setType(0x5);
        request.setLength(payload.length);
        request.setPayload(payload);

        //发送请求
        connection.writeRequest(request);
        //接收返回的响应
        BasicReturns basicReturns = waitResult(queueDeclareArguments.getRid());
        return basicReturns.isOk();
    }

    /*
    * 删除队列
    * */
    public boolean queueDelete(String queueName) throws IOException {
        QueueDeleteArguments queueDeleteArguments = new QueueDeleteArguments();
        queueDeleteArguments.setRid(generateRid());
        queueDeleteArguments.setChannelId(channelId);
        queueDeleteArguments.setQueueName(queueName);
        byte[] payload = BinaryTool.toBytes(queueDeleteArguments);

        Request request = new Request();
        request.setType(0x6);
        request.setLength(payload.length);
        request.setPayload(payload);

        //发送请求
        connection.writeRequest(request);
        //接收返回的响应
        BasicReturns basicReturns = waitResult(queueDeleteArguments.getRid());
        return basicReturns.isOk();

    }

    /*
    * 创建绑定
    * */
    public boolean queueBind(String queueName,String exchangeName,String bindingKey) throws IOException {
        QueueBindArguments queueBindArguments = new QueueBindArguments();
        queueBindArguments.setRid(generateRid());
        queueBindArguments.setChannelId(channelId);
        queueBindArguments.setQueueName(queueName);
        queueBindArguments.setExchangeName(exchangeName);
        queueBindArguments.setBindingKey(bindingKey);
        byte[] payload = BinaryTool.toBytes(queueBindArguments);

        Request request = new Request();
        request.setType(0x7);
        request.setLength(payload.length);
        request.setPayload(payload);

        //发送请求
        connection.writeRequest(request);
        //接收返回的响应
        BasicReturns basicReturns = waitResult(queueBindArguments.getRid());
        return basicReturns.isOk();

    }

    /*
    * 删除绑定
    * */
    public boolean queueUnbind(String queueName,String exchangeName) throws IOException {
        QueueUnBindArguments queueUnBindArguments = new QueueUnBindArguments();
        queueUnBindArguments.setRid(generateRid());
        queueUnBindArguments.setChannelId(channelId);
        queueUnBindArguments.setQueueName(queueName);
        queueUnBindArguments.setExchangeName(exchangeName);
        byte[] payload = BinaryTool.toBytes(queueUnBindArguments);

        Request request = new Request();
        request.setType(0x8);
        request.setLength(payload.length);
        request.setPayload(payload);

        //发送请求
        connection.writeRequest(request);
        //接收返回的响应
        BasicReturns basicReturns = waitResult(queueUnBindArguments.getRid());
        return basicReturns.isOk();
    }


    /*
    * 发送消息
    * */
    public boolean basicPublic(String exchangeName, String routingKey, BasicProperties basicProperties, byte[] body) throws IOException {
        BasicPublicArguments basicPublicArguments = new BasicPublicArguments();
        basicPublicArguments.setRid(generateRid());
        basicPublicArguments.setChannelId(channelId);
        basicPublicArguments.setExchangeName(exchangeName);
        basicPublicArguments.setRoutingKey(routingKey);
        basicPublicArguments.setBasicProperties(basicProperties);
        basicPublicArguments.setBody(body);
        byte[] payload = BinaryTool.toBytes(basicPublicArguments);


        Request request = new Request();
        request.setType(0x9);
        request.setLength(payload.length);
        request.setPayload(payload);

        //发送请求
        connection.writeRequest(request);
        //接收返回的响应
        BasicReturns basicReturns = waitResult(basicPublicArguments.getRid());
        return basicReturns.isOk();
    }

    /*
    * 订阅消息
    * */
    public boolean basicConsume(String queueName, boolean autoAck, Consumer consumer) throws MqException, IOException {
        if(this.consumer != null){
            throw new MqException("不能重复设置回调函数");
        }
        this.consumer = consumer;

        BasicConsumeArguments basicConsumeArguments = new BasicConsumeArguments();
        basicConsumeArguments.setRid(generateRid());
        basicConsumeArguments.setChannelId(channelId);
        basicConsumeArguments.setConsumerTag(channelId);
        basicConsumeArguments.setQueueName(queueName);
        basicConsumeArguments.setAutoAck(autoAck);
        byte[] payload = BinaryTool.toBytes(basicConsumeArguments);

        Request request = new Request();
        request.setType(0xa);
        request.setLength(payload.length);
        request.setPayload(payload);

        //发送请求
        connection.writeRequest(request);
        //接收返回的响应
        BasicReturns basicReturns = waitResult(basicConsumeArguments.getRid());
        return basicReturns.isOk();

    }


    /*
    * 手动应答
    * */
    public boolean basicAck(String queueName,String messageId) throws IOException {
        BasicAckArguments basicAckArguments = new BasicAckArguments();
        basicAckArguments.setRid(generateRid());
        basicAckArguments.setChannelId(channelId);
        basicAckArguments.setQueueName(queueName);
        basicAckArguments.setMessageId(messageId);
        byte[] payload = BinaryTool.toBytes(basicAckArguments);

        Request request = new Request();
        request.setType(0xb);
        request.setLength(payload.length);
        request.setPayload(payload);

        //发送请求
        connection.writeRequest(request);
        //接收返回的响应
        BasicReturns basicReturns = waitResult(basicAckArguments.getRid());
        return basicReturns.isOk();
    }


    /*
    * 阻塞等待服务器的响应（将收到的响应都放在basicReturnsMap哈希表中，遍历如果存在就返回）
    * */
    private BasicReturns waitResult(String rid) {
        BasicReturns basicReturns = null;
        while((basicReturns = basicReturnsMap.get(rid)) ==null){
            synchronized (this) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        //读取走了就需要删除哈希表中的值
        basicReturnsMap.remove(rid);
        return basicReturns;
    }

    public void putReturns(BasicReturns basicReturns) {
        basicReturnsMap.put(basicReturns.getRid(),basicReturns);
        synchronized (this){
            //返回的响应数据回来了，唤醒之前等待的线程
            notifyAll();
        }
    }

    private String generateRid() {
        return "R-"+ UUID.randomUUID();
    }


}
